home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Snippets / 3DBouncer / 3DBouncer.p < prev    next >
Encoding:
Text File  |  1995-12-30  |  21.1 KB  |  770 lines  |  [TEXT/PJMM]

  1. {3D Bouncing Ball Module}
  2. {Written by Daniel C. Stegman, Exodus Software}
  3. {©1992, Exodus Software, All Rights Reserved.}
  4. {This code is intended for use as a After Dark 2.0 ScreenSaver Module.  It is intended for entertainment}
  5. {use, and for the enlightenment of programmers.  It uses fixed math to optimize all of it's 3D calculations,}
  6. {and uses Color on those macs that can support it.  Enjoy!!}
  7. {Dedicated to the Hackers of MacHack, 1992}
  8.  
  9. {Ken Long started the conversion to a stand-alone application, and Ingemar Ragnemalm finished}
  10. {that conversion, plus added sound (with stereo panning).}
  11.  
  12. program GraphicsDemo;
  13.  
  14.     uses
  15. {$IFC UNDEFINED THINK_PASCAL}
  16.         Types, Memory, Packages, Quickdraw, ToolUtils, TextEdit,{}
  17.         Dialogs, Windows, Fonts, 
  18. {$ENDC}
  19.         Sound, CheapSound2, FixMath;
  20.  
  21. {$IFC UNDEFINED THINK_PASCAL}
  22.     var
  23.         white, black, ltGray, gray, dkGray: Pattern;
  24. {$ENDC}
  25.  
  26.     const
  27.         kRedColor = 1;
  28.         kBlueColor = 2;
  29.         kGreenColor = 3;
  30.         kPurpleColor = 4;
  31.         kYellowColor = 5;
  32.         kOrangeColor = 6;
  33.         kWhiteColor = 7;
  34.  
  35.         forceGray = false;
  36.         kNumBalls = 5;
  37.  
  38.     const
  39.         kSmackSoundId = 1000;
  40.         kBounceSoundId = 1001;
  41.  
  42.     type
  43.         ThreeDeePoint = record
  44.                 x: fixed;
  45.                 y: fixed;
  46.                 z: fixed;
  47.             end;
  48.  
  49.         ThreeDeeWorld = record
  50.                 eyeLocation: ThreeDeePoint;
  51.                 screenCenter: ThreeDeePoint;
  52.                 screenRect: Rect;
  53.                 frontTopLeft: ThreeDeePoint;
  54.                 frontTopRight: ThreeDeePoint;
  55.                 frontBotLeft: ThreeDeePoint;
  56.                 frontBotRight: ThreeDeePoint;
  57.                 backTopLeft: ThreeDeePoint;
  58.                 backTopRight: ThreeDeePoint;
  59.                 backBotLeft: ThreeDeePoint;
  60.                 backBotRight: ThreeDeePoint;
  61.                 plutoScaling: real;
  62.             end;
  63.         WorldPtr = ^ThreeDeeWorld;
  64.         WorldHdl = ^WorldPtr;
  65.  
  66.         RectArray = array[1..10] of Rect;
  67.         TheBall = record
  68.                 its3DLoc: ThreeDeePoint;
  69.                 itsShadow: ThreeDeePoint;
  70.                 its2DLoc: point;
  71.                 sh2DLoc: point;
  72.                 oldRect: rect;
  73.                 Old2DRects: RectArray;
  74.                 Oldsh2DRects: RectArray;
  75.                 bounceCount: integer;
  76.                 xVector: fixed;
  77.                 yVector: fixed;
  78.                 zVector: fixed;
  79.                 itsColor: RGBColor;
  80.                 colorCode: integer;
  81.                 gravity: integer;
  82.                 speed: integer;
  83.                 shadowVis: boolean;
  84.             end;
  85.  
  86.         BallArray = array[1..5] of TheBall;
  87.  
  88.         TempStorage = record
  89.                 aBall: TheBall;
  90.                 bBall: TheBall;
  91.                 cBall: TheBall;
  92.                 dBall: TheBall;
  93.                 eBall: TheBall;
  94.                 doColor: boolean;
  95. {forceGray: boolean;}
  96.                 kWhiteRGB: RGBColor;
  97.                 kGrayRGB: RGBColor;
  98.                 kBlackRGB: RGBColor;
  99.                 rotaryColor: integer;
  100.                 numballs: integer;
  101.                 theBalls: ballArray;
  102.                 aBox: ThreeDeeWorld;
  103.             end;
  104.         TempPtr = ^TempStorage;
  105.         TempHdl = ^TempPtr;
  106.  
  107.         IntegerHandle = ^IntegerPtr;
  108.         BooleanPtr = ^Boolean;
  109.         BooleanHandle = ^BooleanPtr;
  110.  
  111.     var
  112.         colorQDAvail: Boolean;
  113.  
  114.     procedure SetThreeDeePoint (var tempPt: ThreeDeePoint; x, y, z: real);
  115.     begin
  116.         tempPt.x := X2Fix(x);
  117.         tempPt.y := X2Fix(y);
  118.         tempPt.z := X2Fix(z);
  119.     end;
  120.  
  121.     procedure ThreeDeeToTwoDee (theWorld: ThreeDeeWorld; aThreeDeePoint: ThreeDeePoint; var a2DPoint: point);
  122.         var
  123.             x1, y1, z1: fixed;
  124.             x2, y2, z2: fixed;
  125.             tempFixed: fixed;
  126.     begin
  127.         with theWorld do
  128.             begin
  129.                 x1 := screenCenter.x - aThreeDeePoint.x;
  130.                 y1 := screenCenter.y - aThreeDeePoint.y;
  131.                 z1 := eyeLocation.z - aThreeDeePoint.z;
  132.                 z2 := screenCenter.z - aThreeDeePoint.z;
  133.  
  134.                 x2 := FixDiv(FixMul(x1, z2), z1);
  135.                 y2 := FixDiv(FixMul(y1, z2), z1);
  136.  
  137.                 x2 := FixMul(x1, FixDiv(z2, z1));
  138.                 y2 := FixMul(y1, FixDiv(z2, z1));
  139.  
  140.                 tempFixed := x2 + screenCenter.x - x1;
  141.                 a2DPoint.h := Fix2Long(tempFixed);
  142.                 tempFixed := y2 + screenCenter.y - y1;
  143.                 a2DPoint.v := Fix2Long(tempFixed);
  144.             end;
  145.     end; {ThreeDeeToTwoDee}
  146.  
  147.     procedure ScaleRect (var tempRect: rect; scaleFactor: real);
  148.         var
  149.             itsCenter: Point;
  150.             itsWidth: integer;
  151.             itsHeight: integer;
  152.     begin
  153.         itsWidth := tempRect.right - tempRect.left;
  154.         itsHeight := tempRect.bottom - tempRect.top;
  155.         itsCenter.h := tempRect.left + itsWidth div 2;
  156.         itsCenter.v := tempRect.top + itsHeight div 2;
  157.  
  158.         tempRect.left := Round(itsCenter.h - (itsWidth * scaleFactor) / 2);
  159.         tempRect.top := Round(itsCenter.v - (itsHeight * scaleFactor) / 2);
  160.  
  161.         tempRect.right := tempRect.left + Round(itsWidth * scaleFactor);
  162.         tempRect.bottom := tempRect.top + Round(itsHeight * scaleFactor);
  163.     end; {ScaleRect}
  164.  
  165.     procedure SetupThreeDeeWorld (var tempWorld: ThreeDeeWorld; monitorRect: rect);
  166.         var
  167.             monitorWidth: real;
  168.             monitorHeight: real;
  169.             monitorDepth: real;
  170.     begin
  171.         with tempWorld do
  172.             begin
  173.                 monitorHeight := monitorRect.bottom - monitorRect.top;
  174.                 monitorWidth := monitorRect.right - monitorRect.left;
  175.                 monitorDepth := monitorWidth;
  176.                 SetThreeDeePoint(eyeLocation, (monitorWidth / 2), 0, -200);
  177.                 SetThreeDeePoint(screenCenter, (monitorWidth / 2), (monitorHeight / 4), -50);
  178.                 SetThreeDeePoint(eyeLocation, (monitorWidth / 2), -200, -500);
  179.                 SetThreeDeePoint(screenCenter, (monitorWidth / 2), (monitorHeight / 4), -100);
  180.  
  181.             {SetThreeDeePoint(eyeLocation, 0, 100, 0);}
  182. {            SetThreeDeePoint(screenCenter, 0, 100, 50);}
  183.                 screenRect := monitorRect;
  184.  
  185.                 SetThreeDeePoint(frontTopLeft, monitorRect.left, monitorRect.top, 0);
  186.                 SetThreeDeePoint(frontTopRight, monitorRect.right, monitorRect.top, 0);
  187.                 SetThreeDeePoint(frontBotLeft, monitorRect.left, monitorRect.bottom, 0);
  188.                 SetThreeDeePoint(frontBotRight, monitorRect.right, monitorRect.bottom, 0);
  189.                 SetThreeDeePoint(backTopLeft, monitorRect.left, monitorRect.top, monitorDepth);
  190.                 SetThreeDeePoint(backTopRight, monitorRect.right, monitorRect.top, monitorDepth);
  191.                 SetThreeDeePoint(backBotLeft, monitorRect.left, monitorRect.bottom, monitorDepth);
  192.                 SetThreeDeePoint(backBotRight, monitorRect.right, monitorRect.bottom, monitorDepth);
  193.  
  194. {    SetThreeDeePoint(frontTopLeft, -monitorWidth, monitorWidth, -monitorWidth);}
  195. {    SetThreeDeePoint(frontTopRight, monitorWidth, monitorWidth, -monitorWidth);}
  196. {    SetThreeDeePoint(frontBotLeft, -monitorWidth, 0, -monitorWidth);}
  197. {    SetThreeDeePoint(frontBotRight, monitorWidth, 0, -monitorWidth);}
  198. {    SetThreeDeePoint(backTopLeft, -monitorWidth, monitorWidth, monitorWidth);}
  199. {    SetThreeDeePoint(backTopRight, monitorWidth, monitorWidth, monitorWidth);}
  200. {    SetThreeDeePoint(backBotLeft, -monitorWidth, 0, monitorWidth);}
  201. {    SetThreeDeePoint(backBotRight, monitorWidth, 0, monitorWidth);    }
  202.             end;
  203.     end; {SetupThreeDeeWorld}
  204.  
  205.     procedure InitializeArray (var itsRectArray: RectArray);
  206.         var
  207.             count: integer;
  208.     begin
  209.         for count := 1 to 10 do
  210.             SetRect(itsRectArray[count], 1, 1, 1, 1);
  211.     end;
  212.  
  213.     procedure ShiftArray (var itsRectArray: RectArray);
  214.         var
  215.             count: integer;
  216.     begin
  217.         for count := 9 downto 1 do
  218.             itsRectArray[count + 1] := itsRectArray[count];
  219.     end;
  220.  
  221.     procedure InitBall (var ballA: TheBall; boxA: ThreeDeeWorld; doColor, forceGray: boolean);
  222.         var
  223.             two: fixed;
  224.             eightTenths: fixed;
  225.             tempFixed: fixed;
  226.             newColor: integer;
  227.  
  228.         function ReturnMagicColor (tempValue: integer): RGBColor;
  229.             var
  230.                 tempColor: RGBColor;
  231.                 newColor: integer;
  232.         begin
  233.             tempColor.red := 0;
  234.             tempColor.green := 0;
  235.             tempColor.blue := 0;
  236.             case tempValue of
  237.                 kRedColor: 
  238.                     begin
  239.                         tempColor.red := -1;
  240.                     end;
  241.                 kBlueColor: 
  242.                     begin
  243.                         tempColor.blue := -1;
  244.                     end;
  245.                 kGreenColor: 
  246.                     begin
  247.                         tempColor.green := -1;
  248.                     end;
  249.                 kPurpleColor: 
  250.                     begin
  251.                         tempColor.red := -1;
  252.                         tempColor.blue := -1;
  253.                     end;
  254.                 kYellowColor: 
  255.                     begin
  256.                         tempColor.red := -1;
  257.                         tempColor.green := -1;
  258.                     end;
  259.                 kOrangeColor: 
  260.                     begin
  261.                         tempColor.red := -1;
  262.                         tempColor.green := 32767;
  263.                     end;
  264.                 otherwise
  265.                     begin
  266.                         tempColor.red := -1;
  267.                         tempColor.blue := -1;
  268.                         tempColor.green := -1;
  269.                     end;
  270.             end;
  271.             ReturnMagicColor := tempColor;
  272.         end;
  273.     begin
  274.         with ballA, boxA do
  275.             begin
  276.                 two := Long2Fix(2);
  277.                 eightTenths := X2Fix(0.8);
  278.  
  279.                 gravity := 0;
  280.                 speed := 5;
  281.                 shadowVis := true;
  282.  
  283. {        gravity := 20;}
  284. {        speed := 40;}
  285. {        shadowVis := TRUE;    }
  286.                 tempFixed := frontTopRight.x - frontTopLeft.x;
  287.  
  288.                 its3DLoc.x := FixDiv(tempFixed, two);
  289.                 its3DLoc.y := frontTopLeft.y;
  290.                 its3DLoc.z := its3DLoc.x;
  291. {Change by Ingemar: Long2Fix(Random mod speed) gives an integer in fixed-point, which is}
  292. {less nice than a full random fixed-point number. Also, I don't want it to be zero.}
  293.                 repeat
  294.                     xVector := Longint(Random) * speed * 2;
  295.                 until xVector <> 0;
  296.                 repeat
  297.                     yVector := Longint(Abs(Random)) * speed * 2;
  298.                 until yVector <> 0;
  299.                 repeat
  300.                     zVector := Longint(Random) * speed * 2;
  301.                 until zVector <> 0;
  302.                 if doColor then
  303.                     begin
  304.                         if forceGray then
  305.                             newColor := kWhiteColor
  306.                         else
  307.                             newColor := Abs(Random) mod 7 + 1;
  308.                         itsColor := ReturnMagicColor(newColor);
  309.                         colorCode := newColor;
  310.                     end;
  311. {    Cannon Settings    }
  312. {    its3DLoc.x := FixDiv(tempFixed, two);}
  313. {    its3DLoc.y := FixMul((frontBotLeft.y - frontTopLeft.y), eightTenths);}
  314. {    its3DLoc.z := frontBotLeft.z;}
  315. {    xVector := Long2Fix(random mod speed);}
  316. {    yVector := Long2Fix(- abs(random mod 30));}
  317. {    zVector := Long2Fix(40 + abs(random mod 30));    }
  318.  
  319.                 bounceCount := 0;
  320.             end;
  321.     end; {InitBall}
  322.  
  323.     function DoInitialize (var storage: Handle; blankRgn: RgnHandle; window: WindowPtr): OSErr;
  324.     {Allocate memory and initialize variables here}
  325.         var
  326.             index: integer;
  327.             bHdl: Handle;
  328.             aRect: rect;
  329.             two: fixed;
  330.             tempFixed: fixed;
  331.             aHdl: TempHdl;
  332.     begin
  333.         aHdl := TempHdl(NewHandle(sizeof(tempStorage)));
  334.         HLock(Handle(aHdl)); {Better lock it. We'll be dereferencing it a lot! /Ingemar}
  335.  
  336.         if aHdl <> nil then
  337.             begin
  338.                 aRect := window^.portRect;
  339.                 SetupThreeDeeWorld(aHdl^^.aBox, aRect);
  340.  
  341.                 with aHdl^^, aBall, aBox do
  342.                     begin
  343.                         numballs := kNumBalls;
  344.                         if numBalls > 5 then
  345.                             numBalls := 5;
  346.  
  347.                         doColor := true;
  348.  
  349.                         kWhiteRGB.red := -1;
  350.                         kWhiteRGB.green := -1;
  351.                         kWhiteRGB.blue := -1;
  352.                         kGrayRGB.red := 32767;
  353.                         kGrayRGB.green := 32767;
  354.                         kGrayRGB.blue := 32767;
  355.                         kBlackRGB.red := 0;
  356.                         kBlackRGB.green := 0;
  357.                         kBlackRGB.blue := 0;
  358.  
  359.                         rotaryColor := 1;
  360.  
  361.                         for index := 1 to numBalls do
  362.                             begin
  363.                                 InitializeArray(theBalls[index].Old2DRects);
  364.                                 InitializeArray(theBalls[index].OldSH2DRects);
  365.                                 InitBall(theBalls[index], aBox, doColor, forceGray);
  366.                                 rotaryColor := rotaryColor + 1;
  367.                             end;
  368.  
  369.                         FillRect(aBox.screenRect, black);
  370.                     end;
  371.                 storage := Handle(aHdl);
  372.                 DoInitialize := noErr;
  373.             end
  374.         else
  375.             DoInitialize := MemError;
  376.     end; {DoInitialize}
  377.  
  378.     function DoBlank (storage: Handle; blankRgn: RgnHandle): OSErr;
  379.     {Blank the screen.  You could also have "credits" appear on the screen here}
  380.     begin
  381.         FillRgn(blankRgn, black);
  382.         DoBlank := noErr;
  383.     end; {DoBlank}
  384.  
  385.     procedure PlaceSound (id: Integer; var loc, box: ThreeDeePoint);
  386.         var
  387.             h, v, hmax, vmax, dist, place: Longint;
  388.     begin
  389.         h := Point(loc.x).v;
  390.         v := Point(loc.z).v;
  391.         hmax := Point(box.x).v;
  392.         vmax := Point(box.z).v;
  393.  
  394.         place := h * 256 div hmax;
  395.         dist := 256 - v * 128 div vmax;
  396.  
  397.         PlaySound(id, (256 - place) * dist div 256, place * dist div 256);
  398.     end; {PlaceSound}
  399.  
  400.     function MoveBall (var ballA: TheBall; boxA: ThreeDeeWorld; doColor, forceGray: boolean): boolean;
  401.         var
  402.             tempFixed: fixed;
  403.             two: fixed;
  404.             anErr: OSErr;
  405.     begin
  406.         with ballA, boxA do
  407.             begin
  408.         { Update the location by applying the vectors }
  409.                 its3DLoc.x := its3DLoc.x + xVector;
  410.                 its3DLoc.y := its3DLoc.y + yVector;
  411.                 its3DLoc.z := its3DLoc.z + zVector;
  412.                 yVector := yVector + X2Fix(gravity / 20);
  413.  
  414.         {    Check for out of bounds    }
  415.                 if its3DLoc.x <= frontTopLeft.x then
  416.                     begin
  417.                         its3DLoc.x := frontTopLeft.x;
  418.                         xVector := -xVector;
  419.                         PlaceSound(kBounceSoundId, its3DLoc, boxA.backBotRight);
  420.                     end
  421.                 else if its3DLoc.x >= frontTopRight.x then
  422.                     begin
  423.                         its3DLoc.x := frontTopRight.x;
  424.                         xVector := -xVector;
  425.                         PlaceSound(kBounceSoundId, its3DLoc, boxA.backBotRight);
  426.                     end;
  427.  
  428.                 if its3DLoc.y <= frontTopLeft.y then
  429.                     begin
  430.                         its3DLoc.y := frontTopLeft.y;
  431.                         yVector := -yVector;
  432.                         PlaceSound(kBounceSoundId, its3DLoc, boxA.backBotRight);
  433.                     end
  434.                 else if its3DLoc.y >= frontBotLeft.y then
  435.                     begin
  436.                         its3DLoc.y := frontBotLeft.y;
  437.                         if gravity > 0 then
  438.                             begin
  439.                                 yVector := FixMul(-yVector, X2Fix(2 / 3));
  440.                                 xVector := FixMul(xVector, X2Fix(3 / 4));
  441.                                 zVector := FixMul(zVector, X2Fix(3 / 4));
  442.                             end
  443.                         else
  444.                             begin
  445.                                 yVector := -yVector;
  446.                             end;
  447.                         bounceCount := bounceCount + 1;
  448.                         if bounceCount = 8 then
  449.                             begin
  450.                                 InitBall(ballA, boxA, doColor, forceGray);
  451.                             end;
  452.                         PlaceSound(kBounceSoundId, its3DLoc, boxA.backBotRight);
  453.                     end;
  454.  
  455.                 if its3DLoc.z <= frontTopLeft.z then
  456.                     begin
  457.                         its3DLoc.z := frontTopLeft.z;
  458.                         zVector := -zVector;
  459.                         PlaceSound(kSmackSoundId, its3DLoc, boxA.backBotRight);
  460.                     end
  461.                 else if its3DLoc.z >= backTopRight.z then
  462.                     begin
  463.                         its3DLoc.z := backTopRight.z;
  464.                         zVector := -zVector;
  465.                         PlaceSound(kSmackSoundId, its3DLoc, boxA.backBotRight);
  466.                     end;
  467.             end;
  468.     end; {MoveBall}
  469.  
  470.     procedure DrawBox (boxA: ThreeDeeWorld; kWhiteRGB, kGrayRGB, kBlackRGB: RGBColor);
  471.         var
  472.             itsRect: rect;
  473.             tempPt1: point;
  474.             tempPt2: point;
  475.     begin
  476.         with boxA do
  477.             begin
  478.                 if colorQDAvail then
  479.                     RGBForeColor(kGrayRGB)
  480.                 else
  481.                     PenPat(white);
  482.  
  483.                 ThreeDeeToTwoDee(boxA, frontTopLeft, tempPt1);
  484.                 ThreeDeeToTwoDee(boxA, frontBotRight, tempPt2);
  485.                 SetRect(itsRect, tempPt1.h, tempPt1.v, tempPt2.h, tempPt2.v);
  486.                 FrameRect(itsRect);
  487.                 ThreeDeeToTwoDee(boxA, backTopLeft, tempPt1);
  488.                 ThreeDeeToTwoDee(boxA, BackBotRight, tempPt2);
  489.                 SetRect(itsRect, tempPt1.h, tempPt1.v, tempPt2.h, tempPt2.v);
  490.                 FrameRect(itsRect);
  491.                 ThreeDeeToTwoDee(boxA, frontTopLeft, tempPt1);
  492.                 MoveTo(tempPt1.h, tempPt1.v);
  493.                 ThreeDeeToTwoDee(boxA, backTopLeft, tempPt1);
  494.                 LineTo(tempPt1.h, tempPt1.v);
  495.                 ThreeDeeToTwoDee(boxA, frontTopRight, tempPt1);
  496.                 MoveTo(tempPt1.h, tempPt1.v);
  497.                 ThreeDeeToTwoDee(boxA, backTopRight, tempPt1);
  498.                 LineTo(tempPt1.h, tempPt1.v);
  499.                 ThreeDeeToTwoDee(boxA, frontBotLeft, tempPt1);
  500.                 MoveTo(tempPt1.h, tempPt1.v);
  501.                 ThreeDeeToTwoDee(boxA, backBotLeft, tempPt1);
  502.                 LineTo(tempPt1.h, tempPt1.v);
  503.                 ThreeDeeToTwoDee(boxA, frontBotRight, tempPt1);
  504.                 MoveTo(tempPt1.h, tempPt1.v);
  505.                 ThreeDeeToTwoDee(boxA, backBotRight, tempPt1);
  506.                 LineTo(tempPt1.h, tempPt1.v);
  507.                 if colorQDAvail then
  508.                     RGBForeColor(kWhiteRGB);
  509.             end;
  510.     end; {DrawBox}
  511.  
  512.     procedure UpdateBall (var ballA: TheBall; aBox: ThreeDeeWorld; kWhiteRGB, kGrayRGB, kBlackRGB: RGBColor);
  513.         var
  514.             itsSize: integer;
  515.             zDepth: fixed;
  516.             itsScale: fixed;
  517.             itsRect: rect;
  518.             shRect: rect;
  519.             tempString1: Str255;
  520.             tempString2: Str255;
  521.  
  522.         procedure RampDownColor (tempColor: RGBColor; colorCode, degradeBy: integer);
  523.             var
  524.                 degradeVal: Integer;
  525.         begin
  526.     {    degradeVal := trunc(65000 / (1.0 * degradeBy));    }
  527.             degradeVal := 65000 div degradeBy;
  528.  
  529.             if colorQDAvail then
  530.                 begin
  531.                     case colorCode of
  532.                         kRedColor: 
  533.                             begin
  534.                                 tempColor.red := degradeVal;
  535.                             end;
  536.                         kBlueColor: 
  537.                             begin
  538.                                 tempColor.blue := degradeVal;
  539.                             end;
  540.                         kGreenColor: 
  541.                             begin
  542.                                 tempColor.green := degradeVal;
  543.                             end;
  544.                         kPurpleColor: 
  545.                             begin
  546.                                 tempColor.red := degradeVal;
  547.                                 tempColor.blue := degradeVal;
  548.                             end;
  549.                         kYellowColor: 
  550.                             begin
  551.                                 tempColor.red := degradeVal;
  552.                                 tempColor.green := degradeVal;
  553.                             end;
  554.                         kOrangeColor: 
  555.                             begin
  556.                                 tempColor.red := degradeVal;
  557.                                 tempColor.green := degradeVal;            { Make it yellow since Orange fades fast }
  558.                             end;
  559.                         otherwise
  560.                             begin
  561.                                 tempColor.red := degradeVal;
  562.                                 tempColor.blue := degradeVal;
  563.                                 tempColor.green := degradeVal;
  564.                             end;
  565.                     end;
  566.                     RGBForeColor(tempColor);
  567.                 end
  568.             else
  569.                 begin
  570.                     case degradeBy of
  571.                         1: 
  572.                             PenPat(white);
  573.                         2..4: 
  574.                             PenPat(ltGray);
  575.                         5..7: 
  576.                             PenPat(gray);
  577.                         otherwise
  578.                             PenPat(dkGray);
  579.                     end;
  580.                 end;
  581.         end; {RampDownColor}
  582.  
  583.     begin {UpdateBall}
  584.         zDepth := abs(aBox.frontTopLeft.z - aBox.backTopLeft.z);
  585.         itsScale := FixDiv((aBox.backTopLeft.z - ballA.its3DLoc.z), aBox.backTopLeft.z);
  586.         itsSize := 4 + Fix2Long(FixMul(itsScale, Long2Fix(20)));
  587.  
  588.         itsRect.left := ballA.its2DLoc.h - (itsSize div 2);
  589.         itsRect.right := ballA.its2DLoc.h + (itsSize div 2);
  590.         itsRect.top := ballA.its2DLoc.v - (itsSize div 2);
  591.         itsRect.bottom := ballA.its2DLoc.v + (itsSize div 2);
  592.         if ballA.shadowVis then
  593.             begin
  594.                 shRect.left := ballA.sh2DLoc.h - (itsSize div 2);
  595.                 shRect.right := ballA.sh2DLoc.h + (itsSize div 2);
  596.                 shRect.top := ballA.sh2DLoc.v - (itsSize div 4);
  597.                 shRect.bottom := ballA.sh2DLoc.v + (itsSize div 4);
  598.             end;
  599.         {RGBForeColor(kWhiteRGB);}
  600.         if colorQDAvail then
  601.             RGBForeColor(kWhiteRGB)
  602.         else
  603.             PenPat(white);
  604.         with ballA do
  605.             begin
  606.                 if colorQDAvail then
  607.                     begin
  608.                         RGBForeColor(kBlackRGB);
  609.                         PaintOval(Old2DRects[10]);
  610.                         PaintOval(Old2DRects[1]);
  611.                         if shadowVis then
  612.                             PaintOval(OldSh2DRects[10]);
  613.                     end
  614.                 else
  615.                     begin
  616.                         FillOval(Old2DRects[10], black);
  617.                         FillOval(Old2DRects[1], black);
  618.                         if shadowVis then
  619.                             FillOval(OldSh2DRects[10], black);
  620.                     end;
  621.  
  622.                 ShiftArray(Old2DRects);
  623.                 Old2DRects[1] := itsRect;
  624.                 ShiftArray(OldSh2DRects);
  625.                 OldSh2DRects[1] := shRect;
  626.  
  627.                 RampDownColor(itsColor, colorCode, 10);
  628.                 FrameOval(Old2DRects[10]);
  629.                 RampDownColor(itsColor, colorCode, 9);
  630.                 FrameOval(Old2DRects[9]);
  631.                 RampDownColor(itsColor, colorCode, 8);
  632.                 FrameOval(Old2DRects[8]);
  633.                 RampDownColor(itsColor, colorCode, 7);
  634.                 FrameOval(Old2DRects[7]);
  635.                 RampDownColor(itsColor, colorCode, 6);
  636.                 FrameOval(Old2DRects[6]);
  637.                 RampDownColor(itsColor, colorCode, 5);
  638.                 FrameOval(Old2DRects[5]);
  639.                 RampDownColor(itsColor, colorCode, 4);
  640.                 FrameOval(Old2DRects[4]);
  641.                 RampDownColor(itsColor, colorCode, 3);
  642.                 FrameOval(Old2DRects[3]);
  643.                 RampDownColor(itsColor, colorCode, 2);
  644.                 FrameOval(Old2DRects[2]);
  645.                 if colorQDAvail then
  646.                     RGBForeColor(itsColor)
  647.                 else
  648.                     PenPat(white);
  649.                 PaintOval(Old2DRects[1]);
  650.  
  651.                 if shadowVis then
  652.                     begin
  653.                         RampDownColor(itsColor, colorCode, 10);
  654.                         FrameOval(OldSh2DRects[10]);
  655.                         RampDownColor(itsColor, colorCode, 9);
  656.                         FrameOval(OldSh2DRects[9]);
  657.                         RampDownColor(itsColor, colorCode, 8);
  658.                         FrameOval(OldSh2DRects[8]);
  659.                         RampDownColor(itsColor, colorCode, 7);
  660.                         FrameOval(OldSh2DRects[7]);
  661.                         RampDownColor(itsColor, colorCode, 6);
  662.                         FrameOval(OldSh2DRects[6]);
  663.                         RampDownColor(itsColor, colorCode, 5);
  664.                         FrameOval(OldSh2DRects[5]);
  665.                         RampDownColor(itsColor, colorCode, 4);
  666.                         FrameOval(OldSh2DRects[4]);
  667.                         RampDownColor(itsColor, colorCode, 3);
  668.                         FrameOval(OldSh2DRects[3]);
  669.                         RampDownColor(itsColor, colorCode, 2);
  670.                         FrameOval(OldSh2DRects[2]);
  671.                         if colorQDAvail then
  672.                             RGBForeColor(itsColor)
  673.                         else
  674.                             PenPat(white);
  675.                         FrameOval(OldSh2DRects[1]);
  676.                     end;
  677.             end;
  678.     end; {UpdateBall}
  679.  
  680.     function DoDrawFrame (storage: Handle; blankRgn: RgnHandle): OSErr;
  681.     {This function is repeatedly called by After Dark.  This is where the main drawing is done.}
  682.         var
  683.             aHdl: TempHdl;
  684.             movement: boolean;
  685.             index: integer;
  686.             waited: longint;
  687.     begin
  688.         aHdl := TempHdl(storage);
  689.         with aHdl^^ do
  690.             begin
  691.                 for index := 1 to numBalls do
  692.                     begin
  693.                         movement := MoveBall(theBalls[index], aBox, doColor, forceGray);
  694.                         ThreeDeeToTwoDee(aBox, theBalls[index].its3DLoc, theBalls[index].its2DLoc);
  695.                         theBalls[index].itsShadow := theBalls[index].its3DLoc;
  696.                         theBalls[index].itsShadow.y := aBox.frontBotRight.y;
  697.                         ThreeDeeToTwoDee(aBox, theBalls[index].itsShadow, theBalls[index].sh2DLoc);
  698.                         UpdateBall(theBalls[index], aBox, kWhiteRGB, kGrayRGB, kBlackRGB);
  699.                     end;
  700.  
  701.                 DrawBox(aBox, kWhiteRGB, kGrayRGB, kBlackRGB);
  702.             end;
  703.         DoDrawFrame := noErr;
  704.     end; {DoDrawFrame}
  705.  
  706.     function DoClose (storage: Handle; blankRgn: RgnHandle): OSErr;
  707.     {Deallocate your memory here.  You can also put something on the screen.}
  708.     begin
  709.         if colorQDAvail then
  710.             begin
  711.                 RGBForeColor(TempHdl(storage)^^.kWhiteRGB);
  712.                 RGBBackColor(TempHdl(storage)^^.kBlackRGB);
  713.             end;
  714.         DisposeHandle(storage);
  715.         DoClose := noErr;
  716.     end; {DoClose}
  717.  
  718.     function DoSetup (blankRgn: RgnHandle; message: integer): OSErr;
  719.     {This is called when the user clicks on a button in the Control Panel.}
  720.     begin
  721.         DoSetup := noErr;
  722.     end; {DoSetup}
  723.  
  724.     var
  725.         myRect: Rect;
  726.         window: WindowPtr;
  727.         storage: Handle;
  728.         err: OSErr;
  729. begin
  730. {$IFC UNDEFINED THINK_PASCAL}
  731.     InitGraf(@qd.thePort);
  732.     InitFonts;
  733.     InitWindows;
  734.     InitMenus;
  735.     TEInit;
  736.     InitDialogs(nil);
  737.     MaxApplZone;
  738.  
  739.     white := qd.white;
  740.     black := qd.black;
  741.     ltGray := qd.ltGray;
  742.     gray := qd.gray;
  743.     dkGray := qd.dkGray;
  744. {$ENDC}
  745.  
  746. {Quickest way to check for Color QD and 32-bit QD:}
  747.     colorQDAvail := NGetTrapAddress($AA1E, toolTrap) <> NGetTrapAddress($A89F, toolTrap);    {Do we have GetCIcon…}
  748.     colorQDAvail := colorQDAvail and (NGetTrapAddress($AB1D, toolTrap) <> NGetTrapAddress($A89F, toolTrap)); {…and 32-bit QD too?}
  749.  
  750. {$IFC UNDEFINED THINK_PASCAL}
  751.     qd.randSeed := TickCount;
  752. {$ELSEC}
  753.     randSeed := TickCount;
  754. {$ENDC}
  755.  
  756.     SetRect(myRect, 40, 40, 450, 350);
  757.     if colorQDAvail then
  758.         window := NewCWindow(nil, myRect, 'Click to exit', true, noGrowDocProc, pointer(-1), true, 0)
  759.     else
  760.         window := NewWindow(nil, myRect, 'Click to exit', true, noGrowDocProc, pointer(-1), true, 0);
  761.     SetPort(window);
  762.  
  763.     err := DoBlank(storage, window^.visRgn);
  764.     err := DoInitialize(storage, window^.visRgn, window);
  765.  
  766.     repeat
  767.         err := DoDrawFrame(storage, window^.visRgn);
  768.     until Button;
  769.     TerminateSound;
  770. end.